
#ifndef LOGFILE_H_
#define LOGFILE_H_

#include <string>
#include <vector>
#include "status.hxx"
#include "dbt.hxx"

class RandomAccessFile;
class SequentialWriter;
class SequentialReader;
class Dbt;

class LogRecord {

public:
	enum RecordType
	{
		PUT = 0,        //log record is created by put/del oeprations
	    COMPACT		//log record is created by compact
	};

    static uint64_t MAGIC;

private:
    //a buffer can be reused to reduce memory allocation, for logrecord deserializetion
    static Dbt* m_serialBuffer;

	uint32_t m_length;
	uint32_t m_crc;//actually, crc is put at the end of record in log file
	uint64_t m_magic;
   
	uint32_t m_type;

	uint64_t m_scn;
	uint32_t m_index;
	uint64_t m_addrvalueNew;
    uint64_t m_addrvalueOld;

	std::string m_dataStoreName;
	std::string m_dbPartitionName;
	
    uint32_t m_loadSizeOld;
    uint32_t m_loadSizeNew;

	Dbt m_dataNew;	

	 //the last 3 entry has layout in the file, as like: [data store name length][data store name]  [partition name length][partition name]  [data length][data]


public:

    LogRecord()
    : m_magic(LogRecord::MAGIC) {    
    }

    //the owner creating the record should call clear to release the m_dataNew
    void clear() {

        if(m_dataNew.data() && m_dataNew.getFlag() == DBT_MALLOC) 
            delete[] m_dataNew.data();
        
        m_dataNew.clear();
    }

    uint32_t calculateRecordLength();

    uint32_t getLength() {return m_length;}
    void setLength(uint32_t len) {m_length = len;}

    uint32_t getCRC() {return m_crc;}
    void setCRC(uint32_t crc) { m_crc = crc;}

    uint64_t getScn(){return m_scn;}
    void setScn(uint64_t scn){m_scn = scn;}

    uint32_t getType() {return m_type;}
    void setType(uint32_t type){m_type = type;}

	uint32_t getIndex() {return m_index; }
	void setIndex(uint32_t index) {m_index = index;}

    uint64_t getAddrNew(){return m_addrvalueNew;}
    void setAddrNew(uint64_t addrvalue){m_addrvalueNew = addrvalue;}	

    uint64_t getAddrOld(){return m_addrvalueOld;}
    void setAddrOld(uint64_t addrvalue){m_addrvalueOld = addrvalue;}

    uint32_t getLoadSizeOld() {return m_loadSizeOld;}
    void setLoadSizeOld(uint32_t loadSizeOld) {m_loadSizeOld = loadSizeOld;}

    uint32_t getLoadSizeNew() {return m_loadSizeNew;}
    void setLoadSizeNew(uint32_t loadSizeNew) {m_loadSizeNew = loadSizeNew;}

    std::string getDsName() {return m_dataStoreName;}
    void setDsName(const std::string& name) {m_dataStoreName = name;}


    std::string getPartitionName(){return m_dbPartitionName;}
    void setPartitionName(const std::string& name) {m_dbPartitionName = name;}

    std::string getDataNew(){return m_dataNew.toString();}
    void setDataNew(const std::string& data){m_dataNew = data;}
    void setDataNew(Dbt& dbt) {m_dataNew = dbt;}

    //calculate crc/length and then serialize to a string
	Dbt* serialize();

	//check whether crc is correct and then deserialize the data
	//return false if checksum is not matched
	bool deserialize(const std::string& data);

    std::string toString();

private:
    void adjustSerialBuffer(uint32_t len);
};


class LogFileHeader {

private:
    static uint64_t MAGIC;

	uint64_t m_version;
	uint64_t m_magic;

public:
    LogFileHeader();

	std::string serialize();
	void deserialize(const std::string& data);

	bool checkHeader();	

    std::string toString();
};

class LogFile {

protected:
	LogFileHeader m_header;
	
    //log full path
    std::string m_fileName;
	uint32_t m_logId;

    RandomAccessFile* m_raf;//for header read/write

public:
    static uint32_t HEADER_SIZE;
	LogFile(const std::string logName, uint32_t  logId);
	virtual ~LogFile();
    
    DbStatus init();

    LogFileHeader getHeader() { return m_header; }
	uint32_t getLogId() { return m_logId; }
	bool isSynced();
	void setSynced();  

protected:	
	
	DbStatus loadHeader();
    DbStatus saveHeader();
};

class LogFileWriter: public LogFile {

protected:
	uint32_t m_capacity;
	SequentialWriter* m_writer; //for appending
	
public:
	LogFileWriter(uint32_t logId, const std::string& logPath, uint32_t capacity);
	~LogFileWriter();

	DbStatus init();

	DbStatus flush();
	DbStatus append(std::string& logRec);
    DbStatus append(Dbt* dbt);

    uint32_t getNextOffset();

    DbStatus closeWriter();
    DbStatus close();
};


class LogFileReader : public LogFile {

private:
	
	SequentialReader * m_reader;
	
public:
	LogFileReader(uint32_t logId, const std::string& logPath);
	~LogFileReader();

	DbStatus init();
	DbStatus getAllRecords(std::vector<LogRecord>& records);

    DbStatus getNextLogRecord(bool& found, LogRecord& rec);

    DbStatus truncateToCurrentOffset();
    DbStatus close();

private:
    DbStatus readInt(uint32_t& data);
};

#endif


